using System;
using System.Collections.Generic;

namespace NetFrameworkExtensions.Utils
{
  public class CachedKeyedWeakReference<TKey, TValue> : KeyedReadonlyReferenceBase<TKey, TValue>
  {
    [NonSerialized]
    private Dictionary<TKey, IReadonlyReference<TValue>> cache;
    private KeyedReadonlyReferenceBase<TKey, TValue> innerReferences;

    public CachedKeyedWeakReference(KeyedReadonlyReferenceBase<TKey, TValue> innerReferences, IEqualityComparer<TKey> keyComparer = null)
    {
      if (innerReferences == null) throw new ArgumentNullException("innerReferences");
      this.innerReferences = innerReferences;
      if (keyComparer != null)
        cache = new Dictionary<TKey, IReadonlyReference<TValue>>(keyComparer);
      else
        cache = new Dictionary<TKey, IReadonlyReference<TValue>>();
    }

    public override TValue this[TKey key]
    {
      get
      {
        IReadonlyReference<TValue> reference;
        if (!cache.TryGetValue(key, out reference))
        {
          lock (cache)
          {
            if (!cache.TryGetValue(key, out reference))
            {
              var newRef = new DelegateReference<TValue>(() => innerReferences[key]);
              reference = newRef;
              cache[key] = reference;
            }
          }
        }
        return reference.Value;
      }
    }
  }
}